<?php

class GReader_API
{

	/**
	 * Our custom request source string
	 *
	 * @since   0.0.1a
	 */
	const REQ_SOURCE = 'IJ GReader API 0.0.1a';

	/**
	 * The username
	 *
	 * @since   0.0.1a
	 * @var     string
	 */
	private $user;

	/**
	 * The password
	 *
	 * @since   0.0.1a
	 * @var     string
	 */
	private $password;

	protected $SID;
	protected $LSID;
	protected $auth;
	protected $token;

	/**
	 * The Google login URL
	 *
	 * @since   0.0.1a
	 * @var     string
	 */
	private static $login_url = 'https://www.google.com/accounts/ClientLogin';

	/**
	 * The Google Reader API URL
	 *
	 * @since   0.0.1a
	 * @var     string
	 */
	private static $api_url = 'http://www.google.com/reader/api/0/';

	/**
	 * Constructor
	 *
	 * @since   0.0.1a
	 * @param   string $user        The GReader username
	 * @param   string $password    The GReader password
	 * @return  GReader_API
	 */
	public function __construct($user, $password)
	{
		$this->user     = $user;
		$this->password = $password;

		$this->connect();
		$this->get_token();
	}

	/**
	 * Does a request
	 *
	 * @since   0.0.1a
	 * @param   string $url                 The URL of the request
	 * @param   string $type                The request type. Whether GET or POST
	 * @param   array $headers              Which headers will be provided to the request
	 * @param   array $fields               Which fields will be provided to the request
	 * @return  array                       Array with the response info and its contents
	 * @throws  InvalidArgumentException    When $url isn't a string or is empty.
	 */
	protected function request($url, $type = 'GET', array $headers = array(), array $fields = array())
	{
		if (empty($url) || !is_string($url))
			throw new InvalidArgumentException("The URL must be supplied and must be a string.");

		$curl = curl_init();
		$type = strtoupper($type) == 'GET' ? 'GET' : 'POST';

		if ($fields)
		{
			if ($type == 'GET')
			{
				$url .= '?' . http_build_query($fields);
			}
			else
			{
				curl_setopt($curl, CURLOPT_POST, true);
				curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($fields));
			}
		}

		if ($headers)
		{
			curl_setopt($curl, CURLOPT_HEADER, true);
			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
		}

		if (strpos($url, 'https://') === 0)
		{
			curl_setopt_array($curl, array(
				CURLOPT_SSL_VERIFYPEER  => true,
				CURLOPT_SSL_VERIFYHOST  => 2,
				CURLOPT_CAINFO          => getcwd() . '/Google-CA.crt'
			));
		}

		curl_setopt_array($curl, array(
			CURLOPT_URL             => $url,
			CURLOPT_RETURNTRANSFER  => 1,
			CURLINFO_HEADER_OUT     => true
		));

		$response = array();
		$response['content'] = curl_exec($curl);
		$response['info']    = curl_getinfo($curl);
		$response['body']    = substr($response['content'], $response['info']['header_size']);

		curl_close($curl);
		return $response;
	}

	/**
	 * Connect to user's Google account
	 *
	 * @since   0.0.1a
	 * @throws  Exception
	 */
	private function connect()
	{
		$response = $this->request(self::$login_url, 'POST', array(), array(
			'service'   => 'reader',
			'Email'     => $this->user,
			'Passwd'    => $this->password,
			'source'    => self::REQ_SOURCE
		));

		// Valid request?
		if ($response['info']['http_code'] == 200)
		{
			$parts = explode("\n", $response['content']);
			$response['content'] = implode('&', $parts);

			$parts = array();
			parse_str($response['content'], $parts);
			$parts = array_map('trim', $parts);

			// Set the properties
			$this->SID = $parts['SID'];
			$this->LSID = $parts['LSID'];
			$this->auth = $parts['Auth'];
		}
		else
		{
			throw new Exception("Error, the server response was " . $response['info']['http_code']);
		}
	}

	/**
	 * Do requests directly to the Google Reader API url.
	 *
	 * @param   string $url         The URL of the request
	 * @param   string $type
	 * @param   array $headers
	 * @param   array $fields
	 * @return  mixed               If $fields has a key <var>output</var>, this method may return an JSON decoded array
	 *                              or an SimpleXMLElement object. Otherwise, the return is simply the body of the response.
	 * @throws  Exception
	 */
	protected function g_request($url, $type = 'GET', array $headers = array(), array $fields = array())
	{
		if (!$this->auth)
			throw new Exception('Cannot do a request without the Google Auth token.');

		$headers[] = 'Content-type: application/x-www-form-urlencoded';
		$headers[] = 'Authorization: GoogleLogin auth=' . $this->auth;
		$url = self::$api_url . $url;

		$response = $this->request($url, $type, $headers, $fields);
		if ($response['info']['http_code'] == 200)
		{
			if (isset($fields['output']))
			{
				switch ($fields['output'])
				{
					case 'xml':
						return new SimpleXMLElement($response['body']);
						break;

					case 'json':
					default:
						return json_decode($response['body']);
						break;
				}
			}
			else
				return $response['body'];
		}
		else
		{
			throw new Exception('Error, the server response was ' . $response['info']['http_code']);
		}
	}

	protected function get_token()
	{
		$this->token = $this->g_request('token');
	}

	public function get_user_info()
	{
		return $this->g_request('user-info', 'GET', array(), array(
			'output'    => 'json',
			'client'    => 'scroll'
		));
	}

}